#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include "shared.h"

// /Applications/Calculator.app/Contents/MacOS/Calculator

#define maxLine 80
pid_t fgpid;

pid_t gpid;

void updateJob2(pid_t pid, int status){
	jobs* temp = job;
	bool found = false;
	while(temp != NULL){
		if (temp->pid == pid){
			found = true;
			temp->status = status;
			printf("%d have been updated with status %",pid, status);
		}
		temp = temp->next;
	}
	if (!found){
		printf("Given process %d not in Jobs list.\n", pid);
	}
}
void sighand(int sig){
	int status;
	pid_t epid;
	switch(sig){
		case SIGINT:
			printf("\nA termination order has been received by %d. Process %d will be terminated. \n",getpid(), fgpid);
			delJob(fgpid);
			kill(fgpid,SIGTERM); return;
		case SIGTSTP:
			printf("\n A suspension order has been received by %d. Process %d will be suspended. \n", getpid(), fgpid);
			kill(fgpid,SIGSTOP);
			bgJob(fgpid); 												/* just list update */
			tcsetpgrp(STDIN_FILENO, gpid = getpgid(fgpid));
			setsid();
			kill(gpid, SIGCONT);
			printf("Process %d will be resumed.\n",gpid); return;
		case SIGCHLD:
			printf("A SIGCHLD signal has been caught.\n");
			epid = waitpid(-1, &status, WUNTRACED | WNOHANG);
				if (WIFSTOPPED(status)){
					updateJob2(epid, WSTOPSIG(status));
				}
	}
}

void exe(char**args, bool bg){
    pid_t pid;
    int status;
    if ((pid = fork()) < 0){
    	perror("fork");
    	exit(1);
    }
    if (pid == 0){
    	printf("If successful, the process ID will be %d.\n",getpid()); fflush(stdout);
    	if (execvp(*args, args) == -1){
    		perror("Wrong command?\n");
    		exit(1);
    	}
    }
    else{ 								/* parent */
    	if(!bg){
    		signal(SIGINT, &sighand); /* parent receives signal */
    		signal(SIGTSTP, &sighand);
    		signal(SIGCHLD, SIG_IGN);
    		fgpid = pid;
    		addJob(pid,1);
    		waitpid(pid,&status,WUNTRACED);
			if (WIFEXITED(status)) delJob(pid);
			else updateJob2(pid, WSTOPSIG(status));
    		signal(SIGINT,SIG_IGN);
    		signal(SIGTSTP, SIG_IGN);
    	}
    	else{
    		fgpid = getpid();
    		addJob(pid, 0);
    		signal(SIGCHLD,&sighand);
    	}
    }
}
void execute(char* input, char** args){
	bool bg = false; int argc = 0; char* delm = " \t\n"; char* token = strtok(input,delm);
	while (token != NULL){
		args[argc] = token; argc++;
		token = strtok(NULL, delm);
	} args[argc] = NULL;
	if (*input != '\n'){
		if (strncmp(args[argc-1],"&",1) == 0) bg = true;
		else if (argc >= 1){
				if (argc > 1 && strncmp(args[0],"kill",4) == 0){
					pid_t tkl = atoi(args[1]);
					delJob(tkl);
					if (tkl != fgpid){ kill(tkl,SIGKILL); printf("Killed %d.\n", tkl); return; }
					else { printf("I'm not allowed to suicide. Use ctrl+d to do so.\n"); return; }
				} else{
					if (strncmp(args[0],"fg",2) == 0){
						pid_t bpid = atoi(args[1]);
						kill(bpid,SIGCONT);		  /* if was sleeping */
			    		signal(SIGINT, &sighand); /* parent receives signal */
			    		signal(SIGTSTP, &sighand);
						wait(&bpid);
						return;
					} else if (strncmp(args[0],"jobs",4) == 0){
								jobs* temp = job;
								printf("-------------------------------------------\n");
								while (temp != NULL){
									printf("Pid: %d | Status: %d\n", temp->pid, temp->status); fflush(stdout);
									temp = temp->next;
								}
								printf("-------------------------------------------\n");
								return;
							}
				  }
		}
		exe(args,bg);
	}
}
int main(){
    char buf[maxLine];
    char *args[maxLine/2+1];

    pid_t gpid = getpid();
    fgpid = gpid;
    addJob(gpid,1);

    if (setpgid(gpid,gpid) == -1) perror("failed to set/join the group.");

    signal(SIGINT,SIG_IGN);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGCHLD,&sighand);

    while(1){
        printf("Prompt -- %d: ", getpid());
        if (fgets(buf,maxLine, stdin) == NULL) {
            printf("\n");
            exit(0);
        } execute(buf, args);
    }
}
